package net.pws.oos.biz.service.impl;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.pws.common.beanutils.BeanUtils;
import net.pws.common.event.ApplicationEventBus;
import net.pws.common.persistence.EntityManager;
import net.pws.common.persistence.UnitOfWork;
import net.pws.common.security.spi.PasswordEncoder;
import net.pws.common.security.spi.event.PermissionChangedEventObject;
import net.pws.common.security.spi.event.PermissionChangedType;
import net.pws.common.security.spi.resource.IResource;
import net.pws.common.security.spi.resource.Resource;
import net.pws.common.security.spi.resource.ResourceDefinitionRepository;
import net.pws.oos.biz.BizException;
import net.pws.oos.biz.EntityUtils;
import net.pws.oos.biz.exception.InvalidPasswordException;
import net.pws.oos.biz.exception.UserNotFoundException;
import net.pws.oos.biz.model.OperateLog;
import net.pws.oos.biz.model.Organization;
import net.pws.oos.biz.model.Permission;
import net.pws.oos.biz.model.Role;
import net.pws.oos.biz.model.User;
import net.pws.oos.biz.query.SystemManageFinder;
import net.pws.oos.biz.query.parameter.UserQueryParameter;
import net.pws.oos.biz.service.SystemManageService;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.tfs.Licence;
import org.apache.commons.tfs.LicenceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "systemManageService")
@Transactional
@SuppressWarnings({ "rawtypes", "unchecked" })
public class DefaultSystemManageService implements SystemManageService {

	@Autowired
	private EntityManager entityManager;

	@Autowired
	private SystemManageFinder systemManageFinder;

	@Autowired
	private PasswordEncoder passwordEncoder;

	public User saveUser(User user) {
		if (StringUtils.isEmpty(user.getId())) {
			// verify lcs
			Licence lcs = LicenceFactory.getInstance().getLicence();
			if (lcs.getUserLimit() > 0) {
				int count = ((Number) entityManager.findUnique(
						"select count(u.id) from User u where u.deleted = ?",
						new Object[] { Boolean.FALSE })).intValue();
				if (count >= lcs.getUserLimit()) {
					throw new BizException(Licence.MSG_USER);
				}
			}

			int count = ((Number) entityManager.findUnique(
					"select count(u.id) from User u where u.name = ?",
					new Object[] { user.getName() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("姓名[{0}]重复",
						user.getName()));
			}

			count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from User u where u.account = ? or u.mobile = ?",
							new Object[] { user.getAccount(), user.getAccount() }))
					.intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("登录账号[{0}]重复",
						user.getAccount()));
			}

			String mobile = user.getMobile();
			if (StringUtils.isNotBlank(mobile)) {
				count = ((Number) entityManager
						.findUnique(
								"select count(u.id) from User u where u.account = ? or u.mobile = ?",
								new Object[] { mobile, mobile })).intValue();
				if (count > 0) {
					throw new BizException(MessageFormat.format("移动电话[{0}]重复",
							mobile));
				}
			}

		} else {
			int count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from User u where u.name = ? and u.id != ?",
							new Object[] { user.getName(), user.getId() }))
					.intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("姓名[{0}]重复",
						user.getName()));
			}

			count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from User u where (u.account = ? or u.mobile = ?) and u.id != ?",
							new Object[] { user.getAccount(),
									user.getAccount(), user.getId() }))
					.intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("登录账号[{0}]重复",
						user.getAccount()));
			}

			String mobile = user.getMobile();
			if (StringUtils.isNotBlank(mobile)) {
				count = ((Number) entityManager
						.findUnique(
								"select count(u.id) from User u where (u.account = ? or u.mobile = ?) and u.id != ?",
								new Object[] { mobile, mobile, user.getId() }))
						.intValue();
				if (count > 0) {
					throw new BizException(MessageFormat.format("移动电话[{0}]重复",
							mobile));
				}
			}

		}

		entityManager.persist(user);
		return user;
	}

	public void deleteUser(String id) {
		User user = entityManager.find(User.class, id);
		if (null == user) {
			return;
		}

		user.setDeleted(Boolean.TRUE);
		String name = user.getName();
		if (name.length() > 46) {
			name = name.substring(0, 46);
		}
		user.setName(name + "|已删除");
		// user.setAccount(user.getAccount() + "|已删除");
		user.setAccount(null);
		user.setMobile(null);
		user.setPassword(null);
		user.setOrganization(null);
		entityManager.persist(user);

		entityManager.nativeExecute(
				"delete from s_user_role where user_id = ?",
				new Object[] { id });
	}

	public void changeUserPassword(String userId, String newPassword) {
		if (StringUtils.isEmpty(newPassword)) {
			throw new InvalidPasswordException("用户密码不能为空！");
		}
		User user = entityManager.find(User.class, userId);
		if (null == user) {
			throw new UserNotFoundException(MessageFormat.format(
					"无效的用户[id={0}]", userId));
		}

		String encodedPassword = encodePassword(newPassword);
		user.setPassword(encodedPassword);
		entityManager.persist(user);
	}

	public void changeUserPassword(String userId, String oldPassword,
			String newPassword) {
		if (StringUtils.isEmpty(oldPassword)
				|| StringUtils.isEmpty(newPassword)) {
			throw new InvalidPasswordException("用户密码不能为空！");
		}
		User user = entityManager.find(User.class, userId);
		if (null == user) {
			throw new UserNotFoundException(MessageFormat.format(
					"无效的用户[id={0}]", userId));
		}

		String encodedOldPassword = encodePassword(oldPassword);
		if (!encodedOldPassword.equals(user.getPassword())) {
			throw new InvalidPasswordException("输入的旧密码不正确！");
		}
		String encodedNewPassword = encodePassword(newPassword);
		user.setPassword(encodedNewPassword);
		entityManager.persist(user);
	}

	// bind
	@Transactional
	public void bindUserRole(String userId, String[] roleIds) {
		UnitOfWork unitOfWork = new UnitOfWork();
		User user = entityManager.find(User.class, userId);
		for (int i = 0; i < roleIds.length; i++) {
			Role role = entityManager.find(Role.class, roleIds[i]);
			user.getRoles().add(role);
			role.getUsers().add(user);
			unitOfWork.registerDirty(role);
		}
		unitOfWork.registerDirty(user);
		entityManager.execute(unitOfWork);

		ApplicationEventBus.getInstance().publishEvent(
				new PermissionChangedEventObject(userId,
						PermissionChangedType.USER_CHANGED));
	}

	public void unbindUserRole(String userId, String[] roleIds) {
		UnitOfWork unitOfWork = new UnitOfWork();
		User user = entityManager.find(User.class, userId);
		for (int i = 0; i < roleIds.length; i++) {
			Role role = entityManager.find(Role.class, roleIds[i]);
			user.getRoles().remove(role);
			role.getUsers().remove(user);
			unitOfWork.registerDirty(role);
		}
		unitOfWork.registerDirty(user);
		entityManager.execute(unitOfWork);

		ApplicationEventBus.getInstance().publishEvent(
				new PermissionChangedEventObject(userId,
						PermissionChangedType.USER_CHANGED));

	}

	public void bindRoleUser(String roleId, String[] userIds) {
		UnitOfWork unitOfWork = new UnitOfWork();
		Role role = entityManager.find(Role.class, roleId);
		for (int i = 0; i < userIds.length; i++) {
			User user = entityManager.find(User.class, userIds[i]);
			user.getRoles().add(role);
			role.getUsers().add(user);
			unitOfWork.registerDirty(user);
		}
		unitOfWork.registerDirty(role);
		entityManager.execute(unitOfWork);

		ApplicationEventBus.getInstance().publishEvent(
				new PermissionChangedEventObject(roleId,
						PermissionChangedType.ROLE_CHANGED));
	}

	public void unbindRoleUser(String roleId, String[] userIds) {
		UnitOfWork unitOfWork = new UnitOfWork();
		Role role = entityManager.find(Role.class, roleId);
		for (int i = 0; i < userIds.length; i++) {
			User user = entityManager.find(User.class, userIds[i]);
			user.getRoles().remove(role);
			role.getUsers().remove(user);
			unitOfWork.registerDirty(user);
		}
		unitOfWork.registerDirty(role);
		entityManager.execute(unitOfWork);

		ApplicationEventBus.getInstance().publishEvent(
				new PermissionChangedEventObject(roleId,
						PermissionChangedType.ROLE_CHANGED));
	}

	public Role saveRole(Role role) {
		if (StringUtils.isEmpty(role.getId())) {
			int count = ((Number) entityManager.findUnique(
					"select count(u.id) from Role u where u.name = ?",
					new Object[] { role.getName() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("名称[{0}]重复",
						role.getName()));
			}
			count = ((Number) entityManager.findUnique(
					"select count(u.id) from Role u where u.code = ?",
					new Object[] { role.getCode() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("编码[{0}]重复",
						role.getCode()));
			}

		} else {
			int count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from Role u where u.name = ? and u.id != ?",
							new Object[] { role.getName(), role.getId() }))
					.intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("名称[{0}]重复",
						role.getName()));
			}
			count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from Role u where u.code = ? and u.id != ?",
							new Object[] { role.getCode(), role.getId() }))
					.intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("编码[{0}]重复",
						role.getCode()));
			}
		}

		entityManager.persist(role);
		return role;
	}

	public void deleteRole(String id) {
		Role role = entityManager.find(Role.class, id);
		if (null == role) {
			return;
		}

		entityManager.nativeExecute(
				"delete from s_permission where role_id = ? ",
				new Object[] { id });
		entityManager.nativeExecute(
				"delete from s_user_role where role_id = ? ",
				new Object[] { id });
		entityManager.nativeExecute("delete from s_role where id = ? ",
				new Object[] { id });
	}

	public void deleteRole(String[] ids) {
		StringBuffer sb = new StringBuffer("(");
		for (int i = 0; i < ids.length; i++) {
			if (i > 0) {
				sb.append(",");
			}
			sb.append("?");
		}
		sb.append(")");
		entityManager.nativeExecute(
				"delete from s_permission where role_id in " + sb.toString(),
				ids);
		entityManager.nativeExecute("delete from s_user_role where role_id in "
				+ sb.toString(), ids);
		entityManager.nativeExecute(
				"delete from s_role where id in " + sb.toString(), ids);
	}

	public Organization saveOrganization(Organization organization) {
		if (StringUtils.isEmpty(organization.getId())) {
			if (organization.getParent() == null) {
				throw new BizException("不能新建顶级建设单位！");
			}

			int count = ((Number) entityManager.findUnique(
					"select count(u.id) from Organization u where u.name = ?",
					new Object[] { organization.getName() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("名称[{0}]重复",
						organization.getName()));
			}
			count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from Organization u where u.shortName = ?",
							new Object[] { organization.getShortName() }))
					.intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("简称[{0}]重复",
						organization.getShortName()));
			}

			count = ((Number) entityManager.findUnique(
					"select count(u.id) from Organization u where u.code = ?",
					new Object[] { organization.getCode() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("编码[{0}]重复",
						organization.getCode()));
			}
			newInheritance(organization);
		} else {
			int count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from Organization u where u.name = ? and u.id != ?",
							new Object[] { organization.getName(),
									organization.getId() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("名称[{0}]重复",
						organization.getName()));
			}
			count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from Organization u where u.shortName = ? and u.id != ?",
							new Object[] { organization.getShortName(),
									organization.getId() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("简称[{0}]重复",
						organization.getShortName()));
			}
			count = ((Number) entityManager
					.findUnique(
							"select count(u.id) from Organization u where u.code = ? and u.id != ?",
							new Object[] { organization.getCode(),
									organization.getId() })).intValue();
			if (count > 0) {
				throw new BizException(MessageFormat.format("编码[{0}]重复",
						organization.getCode()));
			}
			organization = updateInheritance(organization);
		}

		entityManager.persist(organization);
		return organization;
	}

	/**
	 * 更新组织机构时更新树型关系
	 * 
	 * @param organ
	 * @return 返回修改属性的组织机构
	 */
	private Organization updateInheritance(Organization organ) {
		Organization parent = organ.getParent();
		Organization oldOrgan = systemManageFinder.getOrganization(organ
				.getId());
		if (oldOrgan == null) {
			throw new BizException(MessageFormat.format("要修改的建设单位[{0}]不存在！",
					organ.getName()));
		}
		Organization oldParent = oldOrgan.getParent();

		if (parent != null
				&& (parent.getId().equals(organ.getId()) || EntityUtils
						.isSubInnerCode(oldOrgan.getInnerCode(),
								oldOrgan.getDepth(), parent.getInnerCode()))) {
			throw new BizException(MessageFormat.format(
					"要修改的建设单位[{0}]的上级不能是自己或下级！", organ.getName()));
		}

		if (EntityUtils.isEqual(parent, oldParent)) { // 未修改父节点
			organ.setDepth(oldOrgan.getDepth());
			organ.setInnerCode(oldOrgan.getInnerCode());
			BeanUtils.copyProperties(oldOrgan, organ);
		} else { // 修改了父节点
			organ.setDepth(parent == null ? 0 : parent.getDepth() + 1);
			organ.setInnerCode(computeInnerCode(parent));
			BeanUtils.copyProperties(oldOrgan, organ);

			// 更新原父节点
			if (oldParent != null && oldParent.getChildren().size() <= 1) {
				oldParent.setLeaf(true);
				entityManager.persist(oldParent);
			}

			// 更新新父节点
			if (parent != null && parent.isLeaf()) {
				parent.setLeaf(false);
				entityManager.persist(parent);
			}

			// 更新子节点
			updateChildrenInnerCode(oldOrgan);
		}

		return oldOrgan;
	}

	/**
	 * 更新子节点编码与深度
	 * 
	 * @param parent
	 *            父节点
	 */
	private void updateChildrenInnerCode(Organization parent) {
		List<Organization> children = entityManager.findList(
				"from Organization o where o.parent.id = ?",
				new Object[] { parent.getId() });

		for (Organization child : children) {
			String subCode = EntityUtils.updateInnerCode(parent.getInnerCode(),
					parent.getDepth(), child.getInnerCode(), child.getDepth());
			child.setInnerCode(subCode);
			child.setDepth(parent.getDepth() + 1);
			entityManager.persist(child);

			// 更新下级子
			updateChildrenInnerCode(child);
		}
	}

	/**
	 * 新增组织机构时更新树形的关系
	 * 
	 * @param organ
	 */
	private void newInheritance(Organization organ) {
		// 更新父节点
		Organization parent = organ.getParent();
		if (parent != null && parent.isLeaf()) {
			parent.setLeaf(false);
			entityManager.persist(parent);
		}

		organ.setDepth(parent == null ? 0 : parent.getDepth() + 1);
		organ.setInnerCode(computeInnerCode(parent));
	}

	/**
	 * 根据父节点，得到当前节点的编码
	 * 
	 * @param parent
	 * @return
	 */
	private String computeInnerCode(Organization parent) {
		if (parent == null) {
			Organization littleBrother = getMaxCodeOrgan(null);
			if (littleBrother == null) {
				return EntityUtils.newInnerCode(null, 0);
			} else {
				return EntityUtils.plusInnerCode(littleBrother.getInnerCode(),
						littleBrother.getDepth());
			}
		} else {
			Organization littleBrother = getMaxCodeOrgan(parent.getId());
			if (littleBrother == null) {
				return EntityUtils.newInnerCode(parent.getInnerCode(),
						parent.getDepth() + 1);
			} else {
				return EntityUtils.plusInnerCode(littleBrother.getInnerCode(),
						littleBrother.getDepth());
			}
		}
	}

	/**
	 * 得到指定机构的子中内部编码最大的
	 * 
	 * @param parentId
	 * @return
	 */
	private Organization getMaxCodeOrgan(String parentId) {
		List<Object> params = new ArrayList<Object>();
		StringBuilder hql = new StringBuilder("from Organization where ");
		if (StringUtils.isEmpty(parentId)) {
			hql.append(" parent is null ");
		} else {
			hql.append(" parent.id=? ");
			params.add(parentId);
		}
		hql.append(" order by innerCode desc ");
		return (Organization) entityManager.findFirst(hql.toString(),
				params.toArray());
	}

	public void deleteOrganization(String id) {
		Organization organ = entityManager.find(Organization.class, id);
		if (null == organ) {
			return;
		}

		// 判断是否有下级单位
		Set<Organization> children = organ.getChildren();
		for (Organization child : children) {
			if (!BooleanUtils.isTrue(child.getDeleted())) {
				throw new BizException("该单位下面包含有下级单位，不能删除！或者请将下级单位删除后再继续本操作！");
			}
		}

		// 判断是否有用户
		UserQueryParameter queryParameter = new UserQueryParameter();
		queryParameter.setOrganId(organ.getId());
		if (systemManageFinder.countUser(queryParameter) > 0) {
			throw new BizException("该单位下面包含有用户，不能删除！或者请将相关用户删除后再继续本操作！");
		}

		// 更新父节点
		Organization parent = organ.getParent();
		if (parent.getChildren().size() <= 1) {
			parent.setLeaf(true);
			entityManager.persist(parent);
		}

		String name = organ.getName();
		if (name.length() > 46) {
			name = name.substring(0, 46);
		}
		organ.setName(name + "|已删除");
		String code = organ.getCode();
		if (code.length() > 46) {
			code = code.substring(0, 46);
		}
		organ.setCode(code + "|已删除");
		organ.setParent(null);
		organ.setInnerCode("");
		organ.setDeleted(Boolean.TRUE);
		entityManager.persist(organ);
	}

	// resource
	public int getResourceCount() {
		return ResourceDefinitionRepository.getInstance().resourceCount();
	}

	public List<Resource> getResourceChildren(String id) {
		if (StringUtils.isEmpty(id)) {
			return Arrays.asList(ResourceDefinitionRepository.getInstance()
					.getResources());
		}
		return Arrays.asList(ResourceDefinitionRepository.getInstance()
				.getResourceChildren(id));
	}

	public List<Resource> getResourceList() {
		return Arrays.asList(ResourceDefinitionRepository.getInstance()
				.getAllResources());
	}

	public Resource getResource(String id) {
		return ResourceDefinitionRepository.getInstance().getResource(id);
	}

	public List<Map> getMenuPermissionChildren(String id, String roleId) {
		List<Permission> permissions = entityManager.findList(
				" from Permission p where p.role.id = ?",
				new Object[] { roleId });
		if (StringUtils.isEmpty(id)) {
			Resource[] resources = ResourceDefinitionRepository.getInstance()
					.getResources(IResource.MENU);

			return mergeMenuAndPermission(resources, permissions);
		}

		Resource[] resources = ResourceDefinitionRepository.getInstance()
				.getResourceChildren(id, IResource.MENU);
		return mergeMenuAndPermission(resources, permissions);
	}

	public List<Map> getMenuOperationPermissionChildren(String id, String roleId) {
		List<Permission> permissions = entityManager.findList(
				" from Permission p where p.role.id = ?",
				new Object[] { roleId });
		if (StringUtils.isEmpty(id)) {
			Resource[] resources = ResourceDefinitionRepository.getInstance()
					.getResources(IResource.MENU);
			return mergeMenuAndPermission(resources, permissions);
		}

		Resource[] resources = ResourceDefinitionRepository.getInstance()
				.getResourceChildren(id);
		return mergeMenuAndPermission(resources, permissions);
	}

	private List<Map> mergeMenuAndPermission(Resource[] resources,
			List<Permission> permissions) {
		Map<String, Permission> permissionMap = new HashMap<String, Permission>();
		for (Permission permission : permissions) {
			permissionMap.put(permission.getResourceId(), permission);
		}

		List<Map> result = new ArrayList<Map>();
		for (Resource resource : resources) {
			Map item = new HashMap();

			item.put("id", resource.getId());
			item.put("name", resource.getName());
			Permission permission = permissionMap.get(resource.getId());
			if (null != permission) {
				item.put("permissionId", permission.getId());
			}

			result.add(item);
		}
		return result;
	}

	public List saveMenuPermission(String roleId, List menuPermissions) {
		List result = new ArrayList();
		boolean permissionChanged = false;
		for (Iterator iterator = menuPermissions.iterator(); iterator.hasNext();) {
			Map menuPermission = (Map) iterator.next();
			String resourceId = (String) menuPermission.get("id");
			String permissionId = (String) menuPermission.get("permissionId");
			Boolean checked = (Boolean) menuPermission.get("checked");
			if (BooleanUtils.isTrue(checked)
					&& StringUtils.isEmpty(permissionId)) {
				Permission permission = new Permission();
				permission.setRole(entityManager.getReference(Role.class,
						roleId));
				permission.setResourceId(resourceId);
				entityManager.persist(permission);

				Map item = new HashMap();
				item.put("id", resourceId);
				item.put("permissionId", permission.getId());
				result.add(item);

				permissionChanged = true;
			} else if (BooleanUtils.isFalse(checked)
					&& StringUtils.isNotEmpty(permissionId)) {
				entityManager.removeById(Permission.class, permissionId);

				Map item = new HashMap();
				item.put("id", resourceId);
				item.put("permissionId", null);
				result.add(item);

				permissionChanged = true;
			}
		}
		if (permissionChanged) {
			ApplicationEventBus.getInstance().publishEvent(
					new PermissionChangedEventObject(roleId,
							PermissionChangedType.ROLE_CHANGED));
		}
		return result;
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void saveOperateLog(OperateLog operateLog) {
		entityManager.save(operateLog);
	}

	private String encodePassword(String password) {
		if (passwordEncoder == null) {
			return password;
		}
		return passwordEncoder.encode(password);
	}

}
